'use client';

import { useEffect, useRef, useState } from 'react';
import { io, Socket } from 'socket.io-client';
import type { XLayerToken, XLayerStats, XLayerWebSocketEvents } from '@/types/xlayer';

interface UseXLayerWebSocketOptions {
  onTokenUpdate?: (token: Partial<XLayerToken> & { id: string }) => void;
  onNewToken?: (token: XLayerToken) => void;
  onStatsUpdate?: (stats: XLayerStats) => void;
  onContribution?: (data: any) => void;
  onTokenLaunched?: (data: any) => void;
}

export function useXLayerWebSocket(options: UseXLayerWebSocketOptions = {}) {
  const [isConnected, setIsConnected] = useState(false);
  const [connectionError, setConnectionError] = useState<string | null>(null);
  const socketRef = useRef<Socket | null>(null);
  const reconnectTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const reconnectAttempts = useRef(0);
  const maxReconnectAttempts = 5;

  const connect = () => {
    try {
      // Get WebSocket URL from environment or use default
      const wsUrl = process.env.NEXT_PUBLIC_WS_URL || 'ws://localhost:3001';
      
      socketRef.current = io(wsUrl, {
        transports: ['websocket', 'polling'],
        timeout: 20000,
        forceNew: true,
      });

      const socket = socketRef.current;

      socket.on('connect', () => {
        console.log('X Layer WebSocket connected');
        setIsConnected(true);
        setConnectionError(null);
        reconnectAttempts.current = 0;
        
        // Subscribe to X Layer events
        socket.emit('subscribe', { channel: 'xlayer' });
      });

      socket.on('disconnect', (reason) => {
        console.log('X Layer WebSocket disconnected:', reason);
        setIsConnected(false);
        
        if (reason === 'io server disconnect') {
          // Server disconnected, need to reconnect manually
          scheduleReconnect();
        }
      });

      socket.on('connect_error', (error) => {
        console.error('X Layer WebSocket connection error:', error);
        setConnectionError(error.message);
        setIsConnected(false);
        scheduleReconnect();
      });

      // X Layer specific events
      socket.on('xlayer:tokenCreated', (data: XLayerToken) => {
        console.log('New X Layer token created:', data);
        options.onNewToken?.(data);
      });

      socket.on('xlayer:tokenUpdated', (data: Partial<XLayerToken> & { id: string }) => {
        console.log('X Layer token updated:', data);
        options.onTokenUpdate?.(data);
      });

      socket.on('xlayer:statsUpdated', (data: XLayerStats) => {
        console.log('X Layer stats updated:', data);
        options.onStatsUpdate?.(data);
      });

      socket.on('xlayer:contribution', (data) => {
        console.log('New X Layer contribution:', data);
        options.onContribution?.(data);
      });

      socket.on('xlayer:tokenLaunched', (data) => {
        console.log('X Layer token launched:', data);
        options.onTokenLaunched?.(data);
      });

      // Listen for token updates (price changes, new contributions, etc.)
      socket.on('xlayer_token_update', (data) => {
        console.log('X Layer token update:', data);
        if (options.onTokenUpdate && data.id) {
          options.onTokenUpdate(data);
        }
      });

      // Generic event listener for all X Layer events
      socket.onAny((eventName: string, ...args) => {
        if (eventName.startsWith('xlayer:')) {
          console.log(`X Layer WebSocket event: ${eventName}`, args);
        }
      });

    } catch (error) {
      console.error('Failed to create X Layer WebSocket connection:', error);
      setConnectionError('Failed to connect');
      scheduleReconnect();
    }
  };

  const scheduleReconnect = () => {
    if (reconnectAttempts.current >= maxReconnectAttempts) {
      console.log('Max X Layer WebSocket reconnect attempts reached');
      setConnectionError('Connection failed after multiple attempts');
      return;
    }

    const delay = Math.min(1000 * Math.pow(2, reconnectAttempts.current), 30000);
    console.log(`Scheduling X Layer WebSocket reconnect in ${delay}ms`);
    
    reconnectTimeoutRef.current = setTimeout(() => {
      reconnectAttempts.current++;
      connect();
    }, delay);
  };

  const disconnect = () => {
    if (reconnectTimeoutRef.current) {
      clearTimeout(reconnectTimeoutRef.current);
    }
    
    if (socketRef.current) {
      socketRef.current.disconnect();
      socketRef.current = null;
    }
    
    setIsConnected(false);
    setConnectionError(null);
    reconnectAttempts.current = 0;
  };

  // Subscribe to specific token updates
  const subscribeToToken = (tokenId: string) => {
    if (socketRef.current && isConnected) {
      socketRef.current.emit('subscribe', { 
        channel: 'xlayer',
        tokenId 
      });
    }
  };

  // Unsubscribe from specific token updates
  const unsubscribeFromToken = (tokenId: string) => {
    if (socketRef.current && isConnected) {
      socketRef.current.emit('unsubscribe', { 
        channel: 'xlayer',
        tokenId 
      });
    }
  };

  // Subscribe to address-specific events (contributions, etc.)
  const subscribeToAddress = (address: string) => {
    if (socketRef.current && isConnected) {
      socketRef.current.emit('subscribe', { 
        channel: 'xlayer',
        address 
      });
    }
  };

  const unsubscribeFromAddress = (address: string) => {
    if (socketRef.current && isConnected) {
      socketRef.current.emit('unsubscribe', { 
        channel: 'xlayer',
        address 
      });
    }
  };

  // Send custom events
  const emit = (event: string, data: any) => {
    if (socketRef.current && isConnected) {
      socketRef.current.emit(`xlayer:${event}`, data);
    }
  };

  useEffect(() => {
    connect();
    
    return () => {
      disconnect();
    };
  }, []); // Empty dependency array - connect once on mount

  // Cleanup on unmount
  useEffect(() => {
    return () => {
      disconnect();
    };
  }, []);

  return {
    isConnected,
    connectionError,
    connect,
    disconnect,
    subscribeToToken,
    unsubscribeFromToken,
    subscribeToAddress,
    unsubscribeFromAddress,
    emit
  };
}